/*
 * Systemprogrammierung
 * Multiplayer-Quiz
 *
 * Server
 * 
 * main.c: Hauptprogramm des Servers
 */

#include "common/rfc.h"
#include "login.h"
#include "user.h"
#include "catalog.h"
#include <getopt.h>
#include <stdbool.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "common/server_loader_protocol.h"
#include <signal.h>
#include <sys/mman.h>
#include <sys/stat.h>

int port; /**< Portnummer */
int sock; // Socket
const char *program_name = "loader";
pid_t forkResult;
int stdoutPipe[2];
int stdinPipe[2];

void INThandler(int sig) {
	   int c;
	   if(sig != SIGINT)
	      return;
	   else {
	      printf("\nWollen Sie das Programm beenden (j/n) : ");
	      c=getchar();
	      if(c == 'j'){
	    	close(sock);
	         exit (0);
	      }else{
	         return;
	   }
	}
}

/**  Portnummer setzen
 *  param port_str eingegebener Port als String
 */
void set_port(char* port_str) {
	port = atoi(port_str);
}

/**     \brief Portnummer holen */
int get_port() {
	return port;
}

char* kat = { 0 }; /**< Katalogverzeichnis */

/**
 * Verarbeite Startparameter
 * param argc Anzahl der Startparameter
 * param argv Startparameter
 */

void process_commands(int argc, char** argv) {
	// Momentan gewählte Option
	int opt;

	// Standard Port 54321
	char* port = "54321";

	// Schleife für abholen der Startparameter
	while (optind < argc) {

		// Hole nächstes Argument aus argv, berücksichtige Parameter p und c
		opt = getopt(argc, argv, "p:c:d");

		// Keine weiteren Argumente
		if (opt == -1) {
			break;
		}

		// Unterscheiden der Parameter
		switch (opt) {

		// Wenn kein Argument für die Option angegeben wurde
		case ':':
			infoPrint("Server beendet - fehlendes Argument...\n");
			infoPrint("server [-p PORT] -c KATALOGVERZECHNIS\n");
			infoPrint("Beispiel: ./server -p 54321 -c ./catalog\n");
			exit(0);
			break;

			// Option port
		case 'p':
			port = optarg;

			break;

			// Option catalog
		case 'c':
			kat = optarg;
			break;

			// Option nicht bekannt
		case '?':
			break;

		default:
			break;
		}
	}

	// Wenn kein Katalogverzeichnis angegeben wurde
	if (kat == 0) {
		infoPrint("Server beendet - fehlendes Argument...\n");
		infoPrint("server [-p PORT] -c KATALOGVERZECHNIS\n");
		infoPrint("Beispiel: ./server -p 54321 -c ./catalog\n");
		exit(0);
	}

	// Setze Portvariable
	set_port(port);
}

/**  Start des Servers
 *   param argc Anzahl der Startparameter
 *   param argv Startparameter
 */

int main(int argc, char ** argv) {
	setProgName(argv[0]);
	/* ** ** ** Programmstart - abholen der Argumente ** ** */
	process_commands(argc, argv);
	int r = LoaderStart();
	if (r != 0) {
		infoPrint("Fehler beim Starten des Loaders: %i", r);
		return 0;
	}
	int b = LoaderCatalogs();
	if (b != 0) {
		infoPrint("Fehler beim laden der Kataloge: %i", b);
		return 0;
	}

	signal(SIGINT, INThandler);

	/* Ergebnis ausgeben */

	infoPrint("Server Gruppe A1\n");
	init();

	sock = openServerSocket(port);
	if (sock == -1) {
		perror("Server Socket konnte nicht erstellt werden!");
		exit(0);
	}

	pthread_t login_thread;
	int ref = pthread_create(&login_thread, NULL, (void*) login_main(sock),
	NULL);
	if (ref != 0) {
		perror("Login_thread konnte nicht erstellt werden!");
		exit(0);
	}
	return 0;
}

int LoaderStart() {

	if (pipe(stdinPipe) == -1 || pipe(stdoutPipe) == -1) {
		perror("pipe");
		return 3;
	}
	/* Kindprozess abspalten */
	forkResult = fork();
	if (forkResult < 0) /* Fehlerfall */
	{
		perror("fork");
		return 1;
	} else if (forkResult == 0) /* im Kindprozess */
	{
		if (dup2(stdinPipe[0], STDIN_FILENO) == -1) {
			perror("dup2(stdinPipe[0], STDIN_FILENO)");
			return 4;
		}

		/*** NEU: Umleitung der Standardausgabe ***/
		if (dup2(stdoutPipe[1], STDOUT_FILENO) == -1) {
			perror("dup2(stdoutPipe[1], STDOUT_FILENO)");
			return 5;
		}
		/* Anderes Programm in die vorbereitete Prozessumgebung landen */

		close(stdinPipe[0]);
		close(stdoutPipe[1]);
		close(stdinPipe[1]);
		close(stdoutPipe[0]);

		execl(program_name, program_name, "-d", kat, NULL); /* Neues Programm läuft... */
		perror("exec"); /* ...oder auch nicht, dann war's aber ein Fehler */
	} else /* im Elternprozess */
	{

		close(stdinPipe[0]);
		close(stdoutPipe[1]);
	}

	return 0;
}

int LoaderCatalogs() {
	char* message;

	if (write(stdinPipe[1], BROWSE_CMD, strlen(BROWSE_CMD))
			< strlen(BROWSE_CMD)) {
		infoPrint("\nSenden der Nachricht über Pipe fehlgeschlagen: ");
		return 1;
	}
	// Enter
	if (write(stdinPipe[1], "\n", 1) < 1) {
		infoPrint("Senden der Nachricht über Pipe fehlgeschlagen: ");
		return 2;
	}
	int i = 0;
	int err = 0;
	while (err != -1) {
		message = readLine(stdoutPipe[0]);
		err = strcmp(message, "\n");
		if (err != -1) {
			KatalogAdden(message, i);
			i++;
		}
	}

	infoPrint("Kataloge eingelesen: %i\n", i);
	return 0;

}

void LoaderQuestions(char* name) {
	char* message;
	int loaded;
	int shmHandle;
	char* shmData;

	shm_unlink(SHMEM_NAME);
	if (write(stdinPipe[1], LOAD_CMD_PREFIX, strlen(LOAD_CMD_PREFIX))
			< strlen(LOAD_CMD_PREFIX)) {
		infoPrint("\nSenden der Nachricht über Pipe fehlgeschlagen: ");
		exit(0);
	}
	if (write(stdinPipe[1], name, strlen(name)) < strlen(name)) {
		infoPrint("Senden der Nachricht über Pipe fehlgeschlagen: ");
		exit(0);
	}

	// Enter
	if (write(stdinPipe[1], "\n", 1) < 1) {
		infoPrint("Senden der Nachricht über Pipe fehlgeschlagen: ");
		exit(0);
	}
	message = readLine(stdoutPipe[0]);
	if(strcmp(message, LOAD_SUCCESS_PREFIX)!=-1){
		infoPrint("SUCCESS");
		memmove(message, message + sizeof(LOAD_SUCCESS_PREFIX) - 1, 50);
		loaded = atoi(message);
		infoPrint("Loaded: %i", loaded);
	}else{
		infoPrint("ERROR");
		infoPrint(message);
		exit(0);
	}

	shmHandle = shm_open(SHMEM_NAME, O_RDONLY, 0);
	if (shmHandle == -1) {
		infoPrint("Konnte Shared Memory nicht öffnen!");
		exit(0);
	}
	shmData = mmap(NULL, loaded * sizeof(Question), PROT_READ, MAP_SHARED, shmHandle,
			0);

	setShMem(shmData);

	return;

}

